// for utils class
package minicad;
import java.io.Serializable;
import java.awt.*;

interface Shape extends Serializable{
    void draw(Viewer viewer);
    void move(int dx,int dy);
    void set_color(Color c);
    void scale(double s);   // fix center unmoved and do scaling
    double dist(double x,double y); // return square of dist
}

abstract class TwoPtrShape implements Shape{
    int x1,y1,x2,y2;
    Color c = Color.BLACK;
    @Override
    public void move(int dx, int dy) {
        x1 += dx; x2 += dx;
        y1 += dy; y2 += dy; 
    }
    @Override
    public void set_color(Color c) {
        this.c = c;
    }
    @Override
    public void scale(double s) {
       // use transformation to solve it easily
        Complex start = new Complex(x1, y1);
        Complex end = new Complex(x2,y2);
        Complex mid = start.add(end).scale(0.5);
        // mv mid to origin, scale, and mv back
        start = start.add(mid.inv()).scale(s).add(mid);
        end = end.add(mid.inv()).scale(s).add(mid);
        x1 =(int) start.x; y1 =(int) start.y;
        x2 =(int) end.x; y2 =(int) end.y; 
    }

}

class Line extends TwoPtrShape {
    // int x1,y1,x2,y2;
    // Color c = Color.BLACK;
    Line(int x1, int y1, int x2, int y2)
    {
        this.x1 = x1; this.y1 = y1;
        this.x2 = x2; this.y2 = y2;
    }
    @Override
    public String toString() {
        // for debugging
        return String.format("(%d,%d)->(%d,%d)", x1,y1,x2,y2);
    }
    @Override
    public void draw(Viewer viewer) {
        Graphics g = viewer.panel.getGraphics();
        g.setColor(c);
        g.drawLine(x1, y1, x2, y2);
    }
    @Override
    public double dist(double x, double y) {
        // use transformation to solve it easily
        Complex tar = new Complex(x, y);
        return tar.dist2line(x1,y1,x2,y2);
    }
}

class Rect extends TwoPtrShape{
    // two ptrs are diag
    Rect(int x1, int y1, int x2, int y2) {
        this.x1 = x1; this.y1 = y1;
        this.x2 = x2; this.y2 = y2;
    }
    @Override
    public void draw(Viewer viewer) {
        Graphics g = viewer.panel.getGraphics();
        g.setColor(c);
        g.drawLine(x1, y1, x2, y1);
        g.drawLine(x1, y1, x1, y2);
        g.drawLine(x2, y2, x1, y2);
        g.drawLine(x2, y2, x2, y1);
    }
    @Override
    public double dist(double x, double y) {
        Complex tar = new Complex(x, y);
        return tar.dist2rect(x1, y1, x2, y2);
    }
}

class Circ implements Shape{
    int x,y;
    double r;
    Color c = Color.BLACK;
    Circ(int x,int y,double r) {
        this.x = x; this.y = y; this.r = r;
    }
    @Override
    public void draw(Viewer viewer) {
        Graphics g = viewer.panel.getGraphics();
        g.setColor(c);
        int lx =(int)( x - r / Math.sqrt(2) );
        int ly =(int)( y - r / Math.sqrt(2) );
        int width =(int)(r*2);
        int height =(int)(r*2);
        g.drawOval(lx, ly, width, height);
    }
    @Override
    public void move(int dx, int dy) {
        x+=dx; y+=dy;
    }
    @Override
    public void set_color(Color c) {
        this.c = c;
    }
    @Override
    public void scale(double s) {
        r*=s;
    }
    @Override
    public double dist(double x, double y) {
        Complex tar = new Complex(x,y);
        Complex center = new Complex(this.x, this.y);
        tar = tar.add(center.inv());
        double res =Math.sqrt(tar.get_r()) - r ; 
        return res*res;
    }
}


class Text implements Shape{
    int x,y; // center cord
    Color c=Color.BLACK;
    int size=30;// fontsize
    String text;
    Font font; // font is generally unchanged
    int x1,y1,x2,y2; // rect frame of this text, for dist
    Text(String text, int x, int y) {
        this.x = x; this.y = y;
        this.text = text;
        font = new Font(null,Font.PLAIN,size);
    }
    @Override
    public void draw(Viewer viewer) {
        // in draw, we will need to get size info
        Graphics g = viewer.panel.getGraphics();
        g.setColor(c);
        font = new Font(null,Font.PLAIN,size);
        g.setFont(font);
        g.drawString(text, x, y);

        FontMetrics fMetrics = g.getFontMetrics(font);
        // get rec frame info when drawing
        x1 = x;
        y1 = y + fMetrics.getAscent();
        x2 = x+fMetrics.stringWidth(text);
        y2 = y - fMetrics.getDescent();
    }
    @Override
    public void move(int dx, int dy) {
        x+=dx;y+=dy;
    }
    @Override
    public void set_color(Color c) {
        this.c = c;
    }
    @Override
    public void scale(double s) {
        size*=s;
    }
    @Override
    public double dist(double x, double y) {
        // dist is related to details of fontMetrics
        Complex tar = new Complex(x, y);
        return tar.dist2rect(x1, y1, x2, y2);
    }
}

// define Complex for translation and rotation
class Complex{
    double x,y;
    Complex(double x,double y) {
        this.x = x; this.y = y;
    }
    Complex(Complex c) {
        x = c.x; y = c.y;
    }
    @Override
    public String toString() {
        return String.format("Complex(%f,%f)", x,y);
    }
    Complex inv(){
        return new Complex(-x, -y);
    }
    Complex scale(double s){
        return new Complex(s*x, s*y);
    }
    Complex conj() {
        return new Complex(x, -y);
    }
    Complex add(Complex other) {
        return new Complex(this.x+other.x,this.y+other.y);
    }
    Complex mul(Complex other) {
        double rx = x*other.x - y*other.y;
        double ry = x*other.y + y*other.x;
        return new Complex(rx, ry);
    }
    double get_r() {
        /* ret square of r */
        return x*x + y*y;
    }
    double dist2line(double x1,double y1,double x2,double y2)
    {
        // use transformation to solve it easily
        Complex start = new Complex(x1,y1);
        Complex end = new Complex(x2,y2);
        Complex tar = new Complex(this);
        // translation s.t (x1,y1) is origin
        end = end.add(start.inv());
        tar = tar.add(start.inv());
        // rotation s.t end lies on x-axis
        double r = Math.sqrt(end.get_r());
        Complex rotation = end.conj().scale(1./r);
        end = end.mul(rotation);
        tar = tar.mul(rotation);

        double res = -1;
        // do dist now is a lot easier
        if(tar.x < 0) res = tar.get_r(); 
        else if(tar.x < end.x) res = tar.y * tar.y; 
        else res = tar.mul(end.inv()).get_r(); 
        return res; 
    }
    double dist2rect(double x1,double y1,double x2,double y2){
        Complex tar = new Complex(this);
        double[] dist_a = new double[]{
            tar.dist2line(x1, y1, x1, y2),
            tar.dist2line(x2, y1, x2, y2),
            tar.dist2line(x1, y1, x2, y1),
            tar.dist2line(x1, y2, x2, y2),
        };
        double min_dist = dist_a[0]; 
        for (double d : dist_a) {
            if(d*d < min_dist) min_dist = d*d;
        }
        return min_dist;
    }
}